﻿using System;
using System.Data;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;

using BizElements.Core.ProviderComponents;

namespace BizElements.Core
{
    /// <summary>Extends <see cref="IEntityDAO"/> objects.</summary>
    /// <seealso cref="IEntityDAO"/>
    /// <seealso cref="EntityDAO"/>
    public static class EntityDaoExtensions
    {
        // Entity methods.

        #region Parents.

        #region Documentation.
        /// <summary>Selects one existing entity from the database based on the Primary Key and populates entity with the retrieved data.</summary>
        /// <param name="dao">Entity DAO.</param>
        /// <param name="fetchParentEntities">Indicates whether the parent entities defined by the foreign keys 
        /// in the acompanying IDbTable class are to be fetched.</param>
        /// <returns>True if select succeeded; false if primary key is not defined for this table, 
        /// if the primary key values are not set or if the entity does not exist.</returns>
        /// <remarks><p>Parent entities are fetched using the same open connection.</p>
        /// <p>When <c>fetchParentEntities</c> is set to <b>false</b>, the method behavior is the same 
        /// as that of the SelectOne method without parameters.</p></remarks>
        /// <example>
        /// The following example fetches employee and its parent entities.
        /// <code>
        /// public void DisplayEmployeeAndHisManager(int employeeId)
        /// {
        /// 	EmployeesEntity employee = new EmployeesEntity();
        /// 	employee.EmployeeID = employeeId;
        /// 
        /// 	// SelectOne will select the employee and his manager defined by the ReportsTo field.
        /// 	// Both entities are fetched using the same connection.
        /// 	IEntityDAO dao = new EntityDAO(employee);
        /// 	bool employeeExists = dao.SelectOne(true);
        /// 
        /// 	if (employeeExists)
        /// 	{
        /// 		EmployeesEntity manager = employee.EmployeesSuperior;
        /// 		string managerData;
        /// 		if (manager != null)
        /// 			managerData = "Manager: " + manager.FirstName + " " + manager.LastName;
        /// 		else
        /// 			managerData = "Manager: N/A";
        /// 			
        /// 		string employeeData = "Employee: " + employee.FirstName + " " + employee.LastName;		/// 		
        /// 		MessageBox.Show(employeeData + Environment.NewLine + managerData);				
        /// 	}
        /// 	else
        /// 	{
        /// 		MessageBox.Show("Specified employee doesn't exist.");
        /// 	}
        /// }
        /// </code>
        /// </example>
        #endregion
        public static bool SelectOne(this IEntityDAO dao, bool fetchParentEntities)
        {
            return dao.SelectOne(dao.Table.ForeignKeys);
        }

        #region Documentation.
        /// <summary>Fetches all parent entities defined by entity's foreign keys from the data source and stores them into current entity.</summary>
        /// <param name="dao">Entity DAO.</param>
        /// <returns>Parent entities. Some may be <b>null</b> if the required FK fields haven't been set or if the entities 
        /// with the given key values don't exist.</returns>
        #endregion
        public static IEntity[] FetchAllParents(this IEntityDAO dao)
        {
            return dao.FetchParents(dao.Table.ForeignKeys);
        }

        /// <summary>Selects an existing entity and fetch all parents. If parentColumnsToFetch is defined then parents will be  and partially initializes parent entities.</summary>
        /// <param name="dao">Entity DAO with reference to entity whose primary key is set.</param>
        /// <param name="fetchAllParentEntities">Indicates whether to fetch all parent entities.</param>
        /// <param name="parentColumnsToFetch">Optional limited list of parent fields to fetch. If <n>null</n> then full parent entities are fetched and initializes. 
        /// Otherwise only the specified fields are initialized. Has no effect if <b>fetchAllParentEntities</b> argument is set to <b>false</b>.</param>
        /// <returns><b>true</b> if entity exists; otherwise <b>false</b></returns>
        public static bool SelectOne(this IEntityDAO dao, bool fetchAllParentEntities, IEnumerable<IDbColumn> parentColumnsToFetch)
        {
            if (!fetchAllParentEntities)
                return dao.SelectOne();
            if (parentColumnsToFetch == null)
                return dao.SelectOne(dao.Table.ForeignKeys);
            else
                return dao.SelectOne(dao.Table.ForeignKeys, parentColumnsToFetch);
        }

        #endregion

        #region 1:N children.

        #region Documentation.
        /// <summary>
        /// Fetches child entities that reference the current entity.
        /// </summary>
        /// <param name="daoParent">IEntityDAO object that manages parent entity.</param>
        /// <param name="destination">A collection to fill with entities built from data. Will contain entities from the child table whose foreign key matches this entity's primary key.</param>
        /// <param name="relation">Relation between the current entity's table and another (child) table. 
        /// Parent table of the given relation must be this entity's table; otherwise an exception is generated.</param>
        /// <param name="orderBy">Specifies the order in which the entities are added to output collection.</param>
        /// <remarks>An exception is generated if the current entity's primary key value is not set.</remarks>
        /// <example>
        /// The following example fetches a region and all territories it contains.
        /// <code>
        /// RegionEntity easternRegion = new RegionEntity();
        /// easternRegion.RegionID = 1;
        /// EntityDAO daoRegions = new EntityDAO(easternRegion);
        /// 
        /// // Parent (region) and children (territories) data are fetched using the same connection.
        /// daoRegions.ConnectionProvider.OpenConnection();
        /// 
        /// // Fetch region and children defined by the Territories.RegionID foreign key.
        /// daoRegions.SelectOne();
        /// var easternTerritories = new EntityCollection&lt;EmployeesEntity, EmployeesMeta&gt;();
        /// daoRegions.FetchChildren(easternTerritories, new TerritoriesMeta().FK_RegionID, null);
        /// 
        /// // Explicitly close the connection that was openned manually and return it to the connection pool.
        /// daoRegions.ConnectionProvider.CloseConnection();
        /// </code>
        /// </example>
        #endregion
        public static void FetchChildren(this IEntityDAO daoParent, IList destination, DbRelation relation, OrderByClause orderBy)
        {
            daoParent.FetchChildren(destination, relation, null, orderBy);
        }

        #region Documentation.
        /// <summary>
        /// Fetches child entities that reference the current entity.
        /// </summary>
        /// <param name="daoParent">IEntityDAO object that manages parent entity.</param>
        /// <param name="destination">A collection to fill with entities built from data. Will contain entities from the child table whose foreign key matches this entity's primary key.</param>
        /// <param name="relation">Relation between the current entity's table and another (child) table. 
        /// Parent table of the given relation must be this entity's table; otherwise an exception is generated.</param>
        /// <param name="childField">Child field whose value must be equal the given value to be selected.</param>
        /// <param name="childFieldValue">Value of the given child field. May be <b>null</b>.</param>
        /// <param name="orderBy">Specifies the order in which the entities are added to output collection.</param>
        /// <remarks>The method creates a <see cref="SearchCondition"/> that compares the specified column to 
        /// the given value. Only the child rows where the specified column matches the given value are selected.
        /// Very often database is designed so that the child entities are not deleted from the database but are 
        /// marked as deleted instead. This method may be used when only child rows with a specific status value 
        /// (eg. STATUS = 1) are to be fetched.</remarks>
        #endregion
        public static void FetchChildren(this IEntityDAO daoParent, IList destination, DbRelation relation, IDbColumn childField, object childFieldValue, OrderByClause orderBy)
        {
            if (relation.Child.Columns.GetByColumnName(childField.ColumnName) == null)
                throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, Messages.EntityDAO_SpecifiedFieldXDoesntBelongToChildTable, childField.ColumnName));

            SearchCondition childRowsFilter = new SearchCondition(childField, childFieldValue);
            daoParent.FetchChildren(destination, relation, childRowsFilter, orderBy);
        }

        #region Documentation.
        /// <summary>
        /// Fetches child entities that reference the current entity.
        /// </summary>
        /// <param name="daoParent">IEntityDAO object that manages parent entity.</param>
        /// <param name="relation">Relation between the current entity's table and another (child) table. 
        /// Parent table of the given relation must be this entity's table; otherwise an exception is generated.</param>
        /// <param name="orderBy">Specifies the order in which the entities are added to output collection.</param>
        /// <returns>Rows from the child table whose foreign key matches this entity's primary key.</returns>
        /// <remarks>An exception is generated if the current entity's primary key value is not set.</remarks>
        /// <example>
        /// The following example fetches a region and all territories it contains.
        /// <code>
        /// RegionEntity easternRegion = new RegionEntity();
        /// easternRegion.RegionID = 1;
        /// EntityDAO daoRegions = new EntityDAO(easternRegion);
        /// 
        /// // Parent (region) and children (territories) data are fetched using the same connection.
        /// daoRegions.ConnectionProvider.OpenConnection();
        /// 
        /// // Fetch region and children defined by the Territories.RegionID foreign key.
        /// daoRegions.SelectOne();
        /// DataTable easternTerritories = daoRegions.FetchChildren(new TerritoriesMeta().FK_RegionID);
        /// 
        /// // Explicitly close the connection that was openned manually and return it to the connection pool.
        /// easternRegion.ConnectionProvider.CloseConnection();
        /// </code>
        /// </example>
        #endregion
        public static DataTable FetchChildren(this IEntityDAO daoParent, DbRelation relation, OrderByClause orderBy)
        {
            return daoParent.FetchChildren(relation, null, orderBy);
        }

        #region Documentation.
        /// <summary>
        /// Fetches child entities that reference the current entity.
        /// </summary>
        /// <param name="daoParent">IEntityDAO object that manages parent entity.</param>
        /// <param name="relation">Relation between the current entity's table and another (child) table. 
        /// Parent table of the given relation must be this entity's table; otherwise an exception is generated.</param>
        /// <param name="childField">Child field whose value must be equal the given value to be selected.</param>
        /// <param name="childFieldValue">Value of the given child field. May be <b>null</b>.</param>
        /// <param name="orderBy">Specifies the order in which the entities are added to output collection.</param>
        /// <returns>Rows from the child table that reference the current entity and match the given criteria.</returns>
        /// <remarks>The method creates a <see cref="SearchCondition"/> that compares the specified column to 
        /// the given value. Only the child rows where the specified column matches the given value are selected.
        /// Very often database is designed so that the child entities are not deleted from the database but are 
        /// marked as deleted instead. This method may be used when only child rows with a specific status value 
        /// (eg. STATUS = 1) are to be fetched.</remarks>
        #endregion
        public static DataTable FetchChildren(this IEntityDAO daoParent, DbRelation relation, IDbColumn childField, object childFieldValue, OrderByClause orderBy)
        {
            if (relation.Child.Columns.GetByColumnName(childField.ColumnName) == null)
                throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, Messages.EntityDAO_SpecifiedFieldXDoesntBelongToChildTable, childField.ColumnName));

            SearchCondition childRowsFilter = new SearchCondition(childField, childFieldValue);
            return daoParent.FetchChildren(relation, childRowsFilter, orderBy);
        }

        #region Documentation.
        /// <summary>
        /// Fetches child entities that reference the current entity.
        /// </summary>
        /// <param name="daoParent">IEntityDAO object that manages parent entity.</param>
        /// <param name="relation">Relation between the current entity's table and another (child) table. 
        /// Parent table of the given relation must be this entity's table; otherwise an exception is generated.</param>
        /// <param name="orderBy">Specifies the order in which the entities are added to output collection.</param>
        /// <returns>Entities from the child table whose foreign key matches this entity's primary key.</returns>
        /// <remarks>An exception is generated if the current entity's primary key value is not set.</remarks>
        /// <example>
        /// The following example fetches a region and all territories it contains.
        /// <code>
        /// RegionEntity easternRegion = new RegionEntity();
        /// easternRegion.RegionID = 1;
        /// EntityDAO daoRegions = new EntityDAO(easternRegion);
        /// 
        /// // Parent (region) and children (territories) data are fetched using the same connection.
        /// daoRegions.ConnectionProvider.OpenConnection();
        /// 
        /// // Fetch region and children defined by the Territories.RegionID foreign key.
        /// daoRegions.SelectOne();
        /// var easternTerritories = daoRegions.FetchChildren&lt;List&lt;TerritoriesEntity&gt;&gt;(new TerritoriesMeta().FK_RegionID, null);
        /// 
        /// // Explicitly close the connection that was openned manually and return it to the connection pool.
        /// daoRegions.ConnectionProvider.CloseConnection();
        /// </code>
        /// </example>
        #endregion
        public static T FetchChildren<T>(this IEntityDAO daoParent, DbRelation relation, OrderByClause orderBy)
            where T : IList, new()
        {
            T childEntities = new T();
            daoParent.FetchChildren(childEntities, relation, null, orderBy);
            return childEntities;
        }

        #region Documentation.
        /// <summary>
        /// Fetches child entities that reference the current entity.
        /// </summary>
        /// <param name="daoParent">IEntityDAO object that manages parent entity.</param>
        /// <param name="relation">Relation between the current entity's table and another (child) table. 
        /// Parent table of the given relation must be this entity's table; otherwise an exception is generated.</param>
        /// <param name="childField">Child field whose value must be equal the given value to be selected.</param>
        /// <param name="childFieldValue">Value of the given child field. May be <b>null</b>.</param>
        /// <param name="orderBy">Specifies the order in which the entities are added to output collection.</param>
        /// <returns>Entities from the child table whose foreign key matches this entity's primary key.</returns>
        /// <remarks>The method creates a <see cref="SearchCondition"/> that compares the specified column to 
        /// the given value. Only the child rows where the specified column matches the given value are selected.
        /// Very often database is designed so that the child entities are not deleted from the database but are 
        /// marked as deleted instead. This method may be used when only child rows with a specific status value 
        /// (eg. STATUS = 1) are to be fetched.</remarks>
        #endregion
        public static T FetchChildren<T>(this IEntityDAO daoParent, DbRelation relation, IDbColumn childField, object childFieldValue, OrderByClause orderBy)
            where T : IList, new()
        {
            T childEntities = new T();
            SearchCondition childRowsFilter = new SearchCondition(childField, childFieldValue);
            daoParent.FetchChildren(childEntities, relation, childRowsFilter, orderBy);
            return childEntities;
        }

        #region Documentation.
        /// <summary>
        /// Fetches child entities that reference the current entity.
        /// </summary>
        /// <param name="daoParent">IEntityDAO object that manages parent entity.</param>
        /// <param name="relation">Relation between the current entity's table and another (child) table. 
        /// Parent table of the given relation must be this entity's table; otherwise an exception is generated.</param>
        /// <param name="childRowsFilter">Filters which child table records are to be returned. Typically used to filter out inactive records.</param>
        /// <param name="orderBy">Specifies the order in which the entities are added to output collection.</param>
        /// <remarks>The method creates a <see cref="SearchCondition"/> that compares the specified column to 
        /// the given value. Only the child rows where the specified column matches the given value are selected.
        /// Very often database is designed so that the child entities are not deleted from the database but are 
        /// marked as deleted instead. This method may be used when only child rows with a specific status value 
        /// (eg. STATUS = 1) are to be fetched.</remarks>
        #endregion
        public static T FetchChildren<T>(this IEntityDAO daoParent, DbRelation relation, SearchCondition childRowsFilter, OrderByClause orderBy)
            where T : IList, new()
        {
            T childEntities = new T();
            daoParent.FetchChildren(childEntities, relation, childRowsFilter, orderBy);
            return childEntities;
        }

        #endregion

        #region M:N children.

        #region Documentation.
        /// <summary>
        /// Fetches child entities that reference the current entity.
        /// </summary>
        /// <param name="daoParent">IEntityDAO object that manages parent entity.</param>
        /// <param name="destination">A collection to fill with entities built from data. Will contain entities from the child table whose foreign key matches this entity's primary key.</param>
        /// <param name="mnRelation">Many-to-many relation. Current entity's table acts as a parent table,
        /// the other primary table in the relation acts as a child. Resursive M:N relations are not allowed.</param>
        /// <param name="orderBy">Specifies the order in which the entities are added to output collection.</param>
        /// <remarks>When a single database table is used as both the first and the second primary table of an M:N 
        /// relation then it must be explicitly specified which one is used as a parent and which one as a child table. 
        /// This can be accomplished by using one of the overloaded FetchChildren methods that accept a <see cref="PrimaryTableIndex"/> parameter.</remarks>
        /// <example>
        /// The following example fetches children entities using a many-to-many relation.
        /// <code>
        /// public EntityCollection&lt;EmployeesEntity, EmployeesMeta&gt; FetchEmployeeTerritories(EmployeesEntity employee)
        /// {			
        /// 	// Fetch territories that the given employee is in charge of.
        /// 
        /// 	// An employee can be in charge of multiple territories.
        /// 	// Multiple employees can be in charge of a single territory.
        /// 	// Employees and Territories table are related by creating junction rows in EmployeeTerritories table.
        /// 	// EmployeeTerritories table has a primary key which is composed of two foreign keys.
        /// 	// These two one-to-many (1:N) relations can be modeled as a one many-to-many (M:N) relation.
        /// 
        /// 	EmployeeTerritoriesMeta junctionTable = new EmployeeTerritoriesMeta();
        /// 	ManyToManyRelation mnRelation = new ManyToManyRelation(junctionTable.FK_EmployeeID, junctionTable.FK_TerritoryID);
        /// 
        ///		EntityDAO daoEmployees = new EntityDAO(employee);
        ///		var territories = new EntityCollection&lt;EmployeesEntity, EmployeesMeta&gt;();
        /// 	daoEmployees.FetchChildren(territories, mnRelation, null);
        /// 	return territories;
        /// }
        /// </code>
        /// </example>
        #endregion
        public static void FetchChildren(this IEntityDAO daoParent, IList destination, ManyToManyRelation mnRelation, OrderByClause orderBy)
        {
            daoParent.FetchChildren(destination, mnRelation, null, orderBy);
        }

        #region Documentation.
        /// <summary>
        /// Fetches child entities that reference the current entity.
        /// </summary>
        /// <param name="daoParent">IEntityDAO object that manages parent entity.</param>
        /// <param name="destination">A collection to fill with entities built from data. Will contain entities from the child table whose foreign key matches this entity's primary key.</param>
        /// <param name="mnRelation">Many-to-many relation. Current entity's table acts as a parent table,
        /// the other primary table in the relation acts as a child. May be recursive.</param>
        /// <param name="parentSpecifier">Indicates which of the two primary tables in the M:N relation acts as a parent.</param>        
        /// <param name="orderBy">Specifies the order in which the entities are added to output collection.</param>
        /// <remarks>When a single database table is used as both the first and the second primary table of an M:N 
        /// relation then it must be explicitly specified which one is used as a parent and which one as a child table.</remarks>
        #endregion
        public static void FetchChildren(this IEntityDAO daoParent, IList destination, ManyToManyRelation mnRelation, PrimaryTableIndex parentSpecifier, OrderByClause orderBy)
        {
            daoParent.FetchChildren(destination, mnRelation, parentSpecifier, null, orderBy);
        }

        #region Documentation.
        /// <summary>
        /// Fetches child entities that reference the current entity.
        /// </summary>
        /// <param name="daoParent">IEntityDAO object that manages parent entity.</param>
        /// <param name="destination">A collection to fill with entities built from data. Will contain entities from the child table whose foreign key matches this entity's primary key.</param>
        /// <param name="mnRelation">Many-to-many relation. Current entity's table acts as a parent table,
        /// the other primary table in the relation acts as a child. Resursive M:N relations are not allowed.</param>
        /// <param name="childField">Child field whose value must be equal the given value to be selected.</param>
        /// <param name="childFieldValue">Value of the given child field. May be <b>null</b>.</param>
        /// <param name="orderBy">Specifies the order in which the entities are added to output collection.</param>
        /// <remarks><p>The method creates a <see cref="SearchCondition"/> that compares the specified column to 
        /// the given value. Only the child rows where the specified column matches the given value are selected.
        /// Very often database is designed so that the child entities are not deleted from the database but are 
        /// marked as deleted instead. This method may be used when only child rows with a specific status value 
        /// (eg. STATUS = 1) are to be fetched. In addition, a SearchCondition may be created which filters out rows from the junction table. 
        /// See <see cref="ManyToManyRelation.JunctionRowsFilter"/> for more information.</p>
        /// <p>When a single database table is used as both the first and the second primary table of an M:N 
        /// relation then it must be explicitly specified which one is used as a parent and which one as a child table. 
        /// This can be accomplished by using one of the overloaded FetchChildren methods that accept a <see cref="PrimaryTableIndex"/> parameter.</p></remarks>
        #endregion
        public static void FetchChildren(this IEntityDAO daoParent, IList destination, ManyToManyRelation mnRelation, IDbColumn childField, object childFieldValue, OrderByClause orderBy)
        {
            SearchCondition childFilter = new SearchCondition(childField, childFieldValue);
            daoParent.FetchChildren(destination, mnRelation, childFilter, orderBy);
        }

        #region Documentation.
        /// <summary>
        /// Fetches child entities that reference the current entity.
        /// </summary>
        /// <param name="daoParent">IEntityDAO object that manages parent entity.</param>
        /// <param name="destination">A collection to fill with entities built from data. Will contain entities from the child table whose foreign key matches this entity's primary key.</param>
        /// <param name="mnRelation">Many-to-many relation. Current entity's table acts as a parent table,
        /// the other primary table in the relation acts as a child. May be recursive.</param>
        /// <param name="parentSpecifier">Indicates which of the two primary tables in the M:N relation acts as a parent.</param>
        /// <param name="childField">Child field whose value must be equal the given value to be selected.</param>
        /// <param name="childFieldValue">Value of the given child field. May be <b>null</b>.</param>
        /// <param name="orderBy">Specifies the order in which the entities are added to output collection.</param>
        /// <remarks><p>The method creates a <see cref="SearchCondition"/> that compares the specified column to 
        /// the given value. Only the child rows where the specified column matches the given value are selected. 
        /// Very often database is designed so that the child entities are not deleted from the database but are 
        /// marked as deleted instead. This method may be used when only child rows with a specific status value 
        /// (eg. STATUS = 1) are to be fetched. In addition, a SearchCondition may be created which filters out rows 
        /// from the junction table. See <see cref="ManyToManyRelation.JunctionRowsFilter"/> for more information.</p>
        /// <p>When a single database table is used as both the first and the second primary table of an M:N 
        /// relation then it must be explicitly specified which one is used as a parent and which one as a child table.</p></remarks>
        #endregion
        public static void FetchChildren(this IEntityDAO daoParent, IList destination, ManyToManyRelation mnRelation, PrimaryTableIndex parentSpecifier, IDbColumn childField, object childFieldValue, OrderByClause orderBy)
        {
            SearchCondition childFilter = new SearchCondition(childField, childFieldValue);
            daoParent.FetchChildren(destination, mnRelation, parentSpecifier, childFilter, orderBy);
        }

        #region Documentation.
        /// <summary>
        /// Fetches child entities that reference the current entity.
        /// </summary>
        /// <param name="daoParent">IEntityDAO object that manages parent entity.</param>
        /// <param name="mnRelation">Many-to-many relation. Current entity's table acts as a parent table,
        /// the other primary table in the relation acts as a child. Resursive M:N relations are not allowed.</param>
        /// <param name="orderBy">Specifies the order in which the entities are added to output collection.</param>
        /// <returns>Rows form the child table that reference the current entity through junction table of the M:N relation.</returns>
        /// <remarks>When a single database table is used as both the first and the second primary table of an M:N 
        /// relation then it must be explicitly specified which one is used as a parent and which one as a child table. 
        /// This can be accomplished by using one of the overloaded FetchChildren methods that accept a <see cref="PrimaryTableIndex"/> parameter.</remarks>
        /// <example>
        /// The following example fetches children entities using a many-to-many relation.
        /// <code>
        /// public DataTable FetchEmployeeTerritories(EmployeesEntity employee)
        /// {			
        /// 	// Fetch territories that the given employee is in charge of.
        /// 
        /// 	// An employee can be in charge of multiple territories.
        /// 	// Multiple employees can be in charge of a single territory.
        /// 	// Employees and Territories table are related by creating junction rows in EmployeeTerritories table.
        /// 	// EmployeeTerritories table has a primary key which is composed of two foreign keys.
        /// 	// These two one-to-many (1:N) relations can be modeled as a one many-to-many (M:N) relation.
        /// 
        /// 	EmployeeTerritoriesMeta junctionTable = new EmployeeTerritoriesMeta();
        /// 	ManyToManyRelation mnRelation = new ManyToManyRelation(junctionTable.FK_EmployeeID, junctionTable.FK_TerritoryID);
        /// 
        ///		EntityDAO daoEmployees = new EntityDAO(employee);
        /// 	DataTable territories = daoEmployees.FetchChildren(mnRelation);
        /// 	return territories;
        /// }
        /// </code>
        /// </example>
        #endregion
        public static DataTable FetchChildren(this IEntityDAO daoParent, ManyToManyRelation mnRelation, OrderByClause orderBy)
        {
            return daoParent.FetchChildren(mnRelation, null, orderBy);
        }

        #region Documentation.
        /// <summary>
        /// Fetches child entities that reference the current entity.
        /// </summary>
        /// <param name="daoParent">IEntityDAO object that manages parent entity.</param>
        /// <param name="mnRelation">Many-to-many relation. Current entity's table acts as a parent table,
        /// the other primary table in the relation acts as a child. May be recursive.</param>
        /// <param name="parentSpecifier">Indicates which of the two primary tables in the M:N relation acts as a parent.</param>
        /// <param name="orderBy">Specifies the order in which the entities are added to output collection.</param>
        /// <returns>Rows form the child table that reference the current entity through junction table of the M:N relation.</returns>
        /// <remarks>When a single database table is used as both the first and the second primary table of an M:N 
        /// relation then it must be explicitly specified which one is used as a parent and which one as a child table.</remarks>
        #endregion
        public static DataTable FetchChildren(this IEntityDAO daoParent, ManyToManyRelation mnRelation, PrimaryTableIndex parentSpecifier, OrderByClause orderBy)
        {
            return daoParent.FetchChildren(mnRelation, parentSpecifier, null, orderBy);
        }

        #region Documentation.
        /// <summary>
        /// Fetches child entities that reference the current entity.
        /// </summary>
        /// <param name="daoParent">IEntityDAO object that manages parent entity.</param>
        /// <param name="mnRelation">Many-to-many relation. Current entity's table acts as a parent table,
        /// the other primary table in the relation acts as a child. Resursive M:N relations are not allowed.</param>
        /// <param name="childField">Child field whose value must be equal the given value to be selected.</param>
        /// <param name="childFieldValue">Value of the given child field. May be <b>null</b>.</param>
        /// <param name="orderBy">Specifies the order in which the entities are added to output collection.</param>
        /// <returns>Rows form the child table that reference the current entity through 
        /// junction table of the M:N relation and match the given criteria.</returns>
        /// <remarks><p>The method creates a <see cref="SearchCondition"/> that compares the specified column to 
        /// the given value. Only the child rows where the specified column matches the given value are selected.
        /// Very often database is designed so that the child entities are not deleted from the database but are 
        /// marked as deleted instead. This method may be used when only child rows with a specific status value 
        /// (eg. STATUS = 1) are to be fetched. In addition, a SearchCondition may be created which filters out rows from the junction table. 
        /// See <see cref="ManyToManyRelation.JunctionRowsFilter"/> for more information.</p>
        /// <p>When a single database table is used as both the first and the second primary table of an M:N 
        /// relation then it must be explicitly specified which one is used as a parent and which one as a child table. 
        /// This can be accomplished by using one of the overloaded FetchChildren methods that accept a <see cref="PrimaryTableIndex"/> parameter.</p></remarks>
        #endregion
        public static DataTable FetchChildren(this IEntityDAO daoParent, ManyToManyRelation mnRelation, IDbColumn childField, object childFieldValue, OrderByClause orderBy)
        {
            SearchCondition childRowsFilter = new SearchCondition(childField, childFieldValue);
            return daoParent.FetchChildren(mnRelation, childRowsFilter, orderBy);
        }

        #region Documentation.
        /// <summary>
        /// Fetches child entities that reference the current entity.
        /// </summary>
        /// <param name="daoParent">IEntityDAO object that manages parent entity.</param>
        /// <param name="mnRelation">Many-to-many relation. Current entity's table acts as a parent table,
        /// the other primary table in the relation acts as a child. May be recursive.</param>
        /// <param name="parentSpecifier">Indicates which of the two primary tables in the M:N relation acts as a parent.</param>
        /// <param name="childField">Child field whose value must be equal the given value to be selected.</param>
        /// <param name="childFieldValue">Value of the given child field. May be <b>null</b>.</param>
        /// <param name="orderBy">Specifies the order in which the entities are added to output collection.</param>
        /// <returns>Rows form the child table that reference the current entity through junction table of the M:N relation.</returns>
        /// <remarks><p>The method creates a <see cref="SearchCondition"/> that compares the specified column to 
        /// the given value. Only the child rows where the specified column matches the given value are selected. 
        /// Very often database is designed so that the child entities are not deleted from the database but are 
        /// marked as deleted instead. This method may be used when only child rows with a specific status value 
        /// (eg. STATUS = 1) are to be fetched. In addition, a SearchCondition may be created which filters out rows 
        /// from the junction table. See <see cref="ManyToManyRelation.JunctionRowsFilter"/> for more information.</p>
        /// <p>When a single database table is used as both the first and the second primary table of an M:N 
        /// relation then it must be explicitly specified which one is used as a parent and which one as a child table.</p></remarks>
        #endregion
        public static DataTable FetchChildren(this IEntityDAO daoParent, ManyToManyRelation mnRelation, PrimaryTableIndex parentSpecifier, IDbColumn childField, object childFieldValue, OrderByClause orderBy)
        {
            SearchCondition childRowsFilter = new SearchCondition(childField, childFieldValue);
            return daoParent.FetchChildren(mnRelation, parentSpecifier, childRowsFilter, orderBy);
        }

        #region Documentation.
        /// <summary>
        /// Fetches child entities that reference the current entity.
        /// </summary>
        /// <param name="daoParent">IEntityDAO object that manages parent entity.</param>
        /// <param name="mnRelation">Many-to-many relation. Current entity's table acts as a parent table,
        /// the other primary table in the relation acts as a child. Resursive M:N relations are not allowed.</param>
        /// <param name="orderBy">Specifies the order in which the entities are added to output collection.</param>
        /// <returns>Records form the child table that reference the current entity through junction table of the M:N relation.</returns>
        /// <remarks>When a single database table is used as both the first and the second primary table of an M:N 
        /// relation then it must be explicitly specified which one is used as a parent and which one as a child table. 
        /// This can be accomplished by using one of the overloaded FetchChildren methods that accept a <see cref="PrimaryTableIndex"/> parameter.</remarks>
        /// <example>
        /// The following example fetches children entities using a many-to-many relation.
        /// <code>
        /// public EntityCollection&lt;TerritoriesEntity, TerritoriesMeta&gt; FetchTerritoriesForEmployee(EmployeesEntity employee)
        /// {			
        /// 	// Fetch territories that the given employee is in charge of.
        /// 
        /// 	// An employee can be in charge of multiple territories.
        /// 	// Multiple employees can be in charge of a single territory.
        /// 	// Employees and Territories table are related by creating junction rows in EmployeeTerritories table.
        /// 	// EmployeeTerritories table has a primary key which is composed of two foreign keys.
        /// 	// These two one-to-many (1:N) relations can be modeled as a one many-to-many (M:N) relation.
        /// 
        /// 	EmployeeTerritoriesMeta junctionTable = new EmployeeTerritoriesMeta();
        /// 	ManyToManyRelation mnRelation = new ManyToManyRelation(junctionTable.FK_EmployeeID, junctionTable.FK_TerritoryID);
        /// 
        ///		EntityDAO daoEmployees = new EntityDAO(employee);
        /// 	return daoEmployees.FetchChildren&lt;EntityCollection&lt;TerritoriesEntity, TerritoriesMeta&gt;&gt;(mnRelation, null);
        /// }
        /// </code>
        /// </example>
        #endregion
        public static T FetchChildren<T>(this IEntityDAO daoParent, ManyToManyRelation mnRelation, OrderByClause orderBy)
            where T : IList, new()
        {
            T children = new T();
            daoParent.FetchChildren(children, mnRelation, null, orderBy);
            return children;
        }

        #region Documentation.
        /// <summary>
        /// Fetches child entities that reference the current entity.
        /// </summary>
        /// <param name="daoParent">IEntityDAO object that manages parent entity.</param>
        /// <param name="mnRelation">Many-to-many relation. Current entity's table acts as a parent table,
        /// the other primary table in the relation acts as a child. Resursive M:N relations are not allowed.</param>
        /// <param name="childField">Child field whose value must be equal the given value to be selected.</param>
        /// <param name="childFieldValue">Value of the given child field. May be <b>null</b>.</param>
        /// <param name="orderBy">Specifies the order in which the entities are added to output collection.</param>
        /// <returns>Records form the child table that reference the current entity through junction table of the M:N relation.</returns>
        /// <remarks><p>The method creates a <see cref="SearchCondition"/> that compares the specified column to 
        /// the given value. Only the child rows where the specified column matches the given value are selected.
        /// Very often database is designed so that the child entities are not deleted from the database but are 
        /// marked as deleted instead. This method may be used when only child rows with a specific status value 
        /// (eg. STATUS = 1) are to be fetched. In addition, a SearchCondition may be created which filters out rows from the junction table. 
        /// See <see cref="ManyToManyRelation.JunctionRowsFilter"/> for more information.</p>
        /// <p>When a single database table is used as both the first and the second primary table of an M:N 
        /// relation then it must be explicitly specified which one is used as a parent and which one as a child table. 
        /// This can be accomplished by using one of the overloaded FetchChildren methods that accept a <see cref="PrimaryTableIndex"/> parameter.</p></remarks>
        #endregion
        public static T FetchChildren<T>(this IEntityDAO daoParent, ManyToManyRelation mnRelation, IDbColumn childField, object childFieldValue, OrderByClause orderBy)
            where T : IList, new()
        {
            T children = new T();
            SearchCondition childFilter = new SearchCondition(childField, childFieldValue);
            daoParent.FetchChildren(children, mnRelation, childFilter, orderBy);
            return children;
        }

        #region Documentation.
        /// <summary>
        /// Fetches child entities that reference the current entity.
        /// </summary>
        /// <param name="daoParent">IEntityDAO object that manages parent entity.</param>
        /// <param name="mnRelation">Many-to-many relation. Current entity's table acts as a parent table,
        /// the other primary table in the relation acts as a child. May be recursive.</param>
        /// <param name="childRowsFilter">Filters which child table records are to be returned. Typically used to filter out inactive records.</param>
        /// <param name="orderBy">Specifies the order in which the entities are added to output collection.</param>
        /// <returns>Records form the child table that reference the current entity through junction table of the M:N relation.</returns>
        /// <remarks><p>The method uses a <see cref="SearchCondition"/> that compares the specified column to 
        /// the given value. Only the child rows that match the specified criteria are selected. 
        /// Very often database is designed so that the child entities are not deleted from the database but are 
        /// marked as deleted instead. This method may be used when only child rows with a specific status value 
        /// (eg. STATUS = 1) are to be fetched. In addition, a SearchCondition may be created which filters out rows 
        /// from the junction table. See <see cref="ManyToManyRelation.JunctionRowsFilter"/> for more information.</p>
        /// <p>When a single database table is used as both the first and the second primary table of an M:N 
        /// relation then it must be explicitly specified which one is used as a parent and which one as a child table.</p></remarks>
        #endregion
        public static T FetchChildren<T>(this IEntityDAO daoParent, ManyToManyRelation mnRelation, SearchCondition childRowsFilter, OrderByClause orderBy)
            where T : IList, new()
        {
            T children = new T();
            daoParent.FetchChildren(children, mnRelation, childRowsFilter, orderBy);
            return children;
        }

        #region Documentation.
        /// <summary>
        /// Fetches child entities that reference the current entity.
        /// </summary>
        /// <param name="daoParent">IEntityDAO object that manages parent entity.</param>
        /// <param name="mnRelation">Many-to-many relation. Current entity's table acts as a parent table,
        /// the other primary table in the relation acts as a child. May be recursive.</param>
        /// <param name="parentSpecifier">Indicates which of the two primary tables in the M:N relation acts as a parent.</param>        
        /// <param name="orderBy">Specifies the order in which the entities are added to output collection.</param>
        /// <returns>Records form the child table that reference the current entity through junction table of the M:N relation.</returns>
        /// <remarks>When a single database table is used as both the first and the second primary table of an M:N 
        /// relation then it must be explicitly specified which one is used as a parent and which one as a child table.</remarks>
        #endregion
        public static T FetchChildren<T>(this IEntityDAO daoParent, ManyToManyRelation mnRelation, PrimaryTableIndex parentSpecifier, OrderByClause orderBy)
            where T : IList, new()
        {
            T children = new T();
            daoParent.FetchChildren(children, mnRelation, parentSpecifier, null, orderBy);
            return children;
        }

        #region Documentation.
        /// <summary>
        /// Fetches child entities that reference the current entity.
        /// </summary>
        /// <param name="daoParent">IEntityDAO object that manages parent entity.</param>
        /// <param name="mnRelation">Many-to-many relation. Current entity's table acts as a parent table,
        /// the other primary table in the relation acts as a child. May be recursive.</param>
        /// <param name="parentSpecifier">Indicates which of the two primary tables in the M:N relation acts as a parent.</param>
        /// <param name="childField">Child field whose value must be equal the given value to be selected.</param>
        /// <param name="childFieldValue">Value of the given child field. May be <b>null</b>.</param>
        /// <param name="orderBy">Specifies the order in which the entities are added to output collection.</param>
        /// <returns>Records form the child table that reference the current entity through junction table of the M:N relation.</returns>
        /// <remarks><p>The method creates a <see cref="SearchCondition"/> that compares the specified column to 
        /// the given value. Only the child rows where the specified column matches the given value are selected. 
        /// Very often database is designed so that the child entities are not deleted from the database but are 
        /// marked as deleted instead. This method may be used when only child rows with a specific status value 
        /// (eg. STATUS = 1) are to be fetched. In addition, a SearchCondition may be created which filters out rows 
        /// from the junction table. See <see cref="ManyToManyRelation.JunctionRowsFilter"/> for more information.</p>
        /// <p>When a single database table is used as both the first and the second primary table of an M:N 
        /// relation then it must be explicitly specified which one is used as a parent and which one as a child table.</p></remarks>
        #endregion
        public static T FetchChildren<T>(this IEntityDAO daoParent, ManyToManyRelation mnRelation, PrimaryTableIndex parentSpecifier, IDbColumn childField, object childFieldValue, OrderByClause orderBy)
            where T : IList, new()
        {
            T children = new T();
            SearchCondition childFilter = new SearchCondition(childField, childFieldValue);
            daoParent.FetchChildren(children, mnRelation, parentSpecifier, childFilter, orderBy);
            return children;
        }

        #region Documentation.
        /// <summary>
        /// Fetches child entities that reference the current entity.
        /// </summary>
        /// <param name="daoParent">IEntityDAO object that manages parent entity.</param>
        /// <param name="mnRelation">Many-to-many relation. Current entity's table acts as a parent table,
        /// the other primary table in the relation acts as a child. May be recursive.</param>
        /// <param name="parentSpecifier">Indicates which of the two primary tables in the M:N relation acts as a parent.</param>
        /// <param name="childRowsFilter">Filters which child table records are to be returned. Typically used to filter out inactive records.</param>
        /// <param name="orderBy">Specifies the order in which the entities are added to output collection.</param>
        /// <returns>Records form the child table that reference the current entity through junction table of the M:N relation.</returns>
        /// <remarks><p>The method uses a <see cref="SearchCondition"/> that compares the specified column to 
        /// the given value. Only the child rows that match the specified criteria are selected. 
        /// Very often database is designed so that the child entities are not deleted from the database but are 
        /// marked as deleted instead. This method may be used when only child rows with a specific status value 
        /// (eg. STATUS = 1) are to be fetched. In addition, a SearchCondition may be created which filters out rows 
        /// from the junction table. See <see cref="ManyToManyRelation.JunctionRowsFilter"/> for more information.</p>
        /// <p>When a single database table is used as both the first and the second primary table of an M:N 
        /// relation then it must be explicitly specified which one is used as a parent and which one as a child table.</p></remarks>
        #endregion
        public static T FetchChildren<T>(this IEntityDAO daoParent, ManyToManyRelation mnRelation, PrimaryTableIndex parentSpecifier, SearchCondition childRowsFilter, OrderByClause orderBy)
            where T : IList, new()
        {
            T children = new T();
            daoParent.FetchChildren(children, mnRelation, parentSpecifier, childRowsFilter, orderBy);
            return children;
        }

        #endregion

        #region Hierarchical queries and updates.

        #region Documentation.
        /// <summary>
        /// Selects all rows in the hierarchy starting with the current entity.
        /// </summary>
        /// <param name="daoRoot">IEntityDAO object that manages the entity which represents a starting node, i.e. a parent in hierarchy.</param>
        /// <param name="recursiveRelation">Recursive relation which defines a hierarchy.</param>
        /// <param name="leafFilter">The search condition which restricts the rows returned. Often a criteria which filters out deactivated records.</param>
        /// <returns>All rows in the hierarchy under the current entity, including the current entity.</returns>
        /// <example>The following example fetches all rows in the employee hierarchy starting with the specified employee/manager.
        /// <code>
        /// public DataTable FetchHierarchy(int managerId)
        /// {
        /// 	EmployeesEntity manager = new EmployeesEntity(managerId);
        /// 	IEntityDAO dao = new EntityDAO(manager);
        /// 	DataTable hierarchy = dao.SelectTree(manager.Table.FK_ReportsTo, null);
        /// 	return hierarchy;
        /// }
        /// </code>
        /// </example>
        #endregion
        public static DataTable SelectTree(this IEntityDAO daoRoot, DbRelation recursiveRelation, SearchCondition leafFilter)
        {
            return daoRoot.SelectTree(recursiveRelation, leafFilter, 0, int.MaxValue);
        }

        #region Documentation.
        /// <summary>
        /// Selects rows in the hierarchy starting with the current entity.
        /// </summary>
        /// <param name="daoRoot">IEntityDAO object that manages the entity which represents a starting node, i.e. a parent in hierarchy.</param>
        /// <param name="recursiveRelation">Recursive relation which defines a hierarchy.</param>
        /// <param name="leafFilter">The search condition which restricts the rows returned. Often a criteria which filters out deactivated records.</param>
        /// <param name="maxLevels">Maximum number of tree levels to fetch. One specifies that only current entity is fetched, 
        /// two specifies that current entity and entities directly beneath it are fetched etc.</param>
        /// <returns>Rows in the hierarchy under the current entity, including the current entity.</returns>
        /// <example>The following example fetches rows from the first three levels in the employee hierarchy starting with the specified employee/manager.
        /// <code>
        /// public DataTable FetchHierarchy(int managerId)
        /// {
        /// 	EmployeesEntity manager = new EmployeesEntity(managerId);
        /// 	IEntityDAO dao = new EntityDAO(manager);
        /// 	DataTable hierarchy = dao.SelectTree(manager.Table.FK_ReportsTo, null, 3);
        /// 	return hierarchy;
        /// }
        /// </code>
        /// </example>
        #endregion
        public static DataTable SelectTree(this IEntityDAO daoRoot, DbRelation recursiveRelation, SearchCondition leafFilter, int maxLevels)
        {
            if (maxLevels < 0)
                throw new ArgumentOutOfRangeException("maxLevels", Messages.EntityDAO_NegativeValueIsNotAllowed);

            return daoRoot.SelectTree(recursiveRelation, leafFilter, 0, maxLevels - 1);
        }

        #region Documentation.
        /// <summary>
        /// Updates all rows in the hierarchy starting with the current entity.
        /// </summary>
        /// <param name="daoRoot">IEntityDAO object that manages the entity which represents a starting node, i.e. a parent in hierarchy.</param>
        /// <param name="recursiveRelation">Recursive relation which defines a hierarchy.</param>
        /// <param name="setExpressions">The list of columns to be updated and expressions that compute/contain the new values.</param>
        /// <param name="leafFilter">The search condition which limits the number of rows that are updated. Often a criteria which filters out deactivated records.</param>
        /// <returns>Number of rows affected.</returns>
        /// <example>The following example updates all rows in the employee hierarchy starting with the specified employee/manager.
        /// <code>
        /// public int MoveHierarchyToCountry(int managerId, string newCountry)
        ///	{
        ///		EmployeesEntity manager = new EmployeesEntity(managerId);
        ///		UpdateList setNewCountry = new UpdateList(manager.Table.Country, newCountry);
        ///		IEntityDAO dao = new EntityDAO(manager);
        ///		int rowsAffected = dao.UpdateTree(manager.Table.FK_ReportsTo, setNewCountry, null);
        ///		return rowsAffected;
        ///	}
        /// </code>
        /// </example>
        #endregion
        public static int UpdateTree(this IEntityDAO daoRoot, DbRelation recursiveRelation, UpdateList setExpressions, SearchCondition leafFilter)
        {
            return daoRoot.UpdateTree(recursiveRelation, setExpressions, leafFilter, 0, int.MaxValue);
        }

        #region Documentation.
        /// <summary>
        /// Updates rows in the hierarchy starting with the current entity.
        /// </summary>
        /// <param name="daoRoot">IEntityDAO object that manages the entity which represents a starting node, i.e. a parent in hierarchy.</param>
        /// <param name="recursiveRelation">Recursive relation which defines a hierarchy.</param>
        /// <param name="setExpressions">The list of columns to be updated and expressions that compute/contain the new values.</param>
        /// <param name="leafFilter">The search condition which limits the number of rows that are updated. Often a criteria which filters out deactivated records.</param>
        /// <param name="maxLevels">Maximum number of tree levels to affect. One specifies that only current entity is updated, 
        /// two specifies that current entity and entities directly beneath it are updated etc.</param>
        /// <returns>Number of rows affected.</returns>
        /// <example>The following example updates rows in the first three levels of the employee hierarchy starting with the specified employee/manager.
        /// <code>
        /// public int MoveHierarchyToCountry(int managerId, string newCountry)
        ///	{
        ///		EmployeesEntity manager = new EmployeesEntity(managerId);
        ///		UpdateList setNewCountry = new UpdateList(manager.Table.Country, newCountry);
        ///		IEntityDAO dao = new EntityDAO(manager);
        ///		int rowsAffected = dao.UpdateTree(manager.Table.FK_ReportsTo, setNewCountry, null, 3);
        ///		return rowsAffected;
        ///	}
        /// </code>
        /// </example>
        #endregion
        public static int UpdateTree(this IEntityDAO daoRoot, DbRelation recursiveRelation, UpdateList setExpressions, SearchCondition leafFilter, int maxLevels)
        {
            if (maxLevels < 0)
                throw new ArgumentOutOfRangeException("maxLevels", Messages.EntityDAO_NegativeValueIsNotAllowed);

            return daoRoot.UpdateTree(recursiveRelation, setExpressions, leafFilter, 0, maxLevels - 1);
        }

        #region Documentation.
        /// <summary>
        /// Counts all rows in the hierarchy starting with the current entity.
        /// </summary>
        /// <param name="daoRoot">IEntityDAO object that manages the entity which represents a starting node, i.e. a parent in hierarchy.</param>
        /// <param name="recursiveRelation">Recursive relation which defines a hierarchy.</param>
        /// <param name="leafFilter">The search condition which restricts the rows counted. Often a criteria which filters out deactivated records.</param>
        /// <returns>Total row count in the hierarchy.</returns>
        /// <example>The following example counts all rows in the employee hierarchy starting with the specified employee/manager.
        /// <code>
        ///	public int CountAllSubordinates(int managerId)
        ///	{
        ///		EmployeesEntity manager = new EmployeesEntity(managerId);
        ///		IEntityDAO dao = new EntityDAO(manager);
        ///		int totalEmployeeCount = dao.CountTree(manager.Table.FK_ReportsTo, null);
        ///		return totalEmployeeCount;
        ///	}
        /// </code>
        /// </example>
        #endregion
        public static int CountTree(this IEntityDAO daoRoot, DbRelation recursiveRelation, SearchCondition leafFilter)
        {
            return daoRoot.CountTree(recursiveRelation, leafFilter, 0, int.MaxValue);
        }

        #region Documentation.
        /// <summary>
        /// Counts rows in the hierarchy starting with the current entity.
        /// </summary>
        /// <param name="daoRoot">IEntityDAO object that manages the entity which represents a starting node, i.e. a parent in hierarchy.</param>
        /// <param name="recursiveRelation">Recursive relation which defines a hierarchy.</param>
        /// <param name="leafFilter">The search condition which restricts the rows counted. Often a criteria which filters out deactivated records.</param>
        /// <param name="maxLevels">Maximum number of tree levels to process. One specifies that only current entity is counted, 
        /// two specifies that current entity and entities directly beneath it are counted etc.</param>
        /// <returns>Total row count in the hierarchy.</returns>
        /// <example>The following example counts all rows in the first three levels of the employee hierarchy starting with the specified employee/manager.
        /// <code>
        ///	public int CountAllSubordinates(int managerId)
        ///	{
        ///		EmployeesEntity manager = new EmployeesEntity(managerId);
        ///		IEntityDAO dao = new EntityDAO(manager);
        ///		int totalEmployeeCount = dao.CountTree(manager.Table.FK_ReportsTo, null, 3);
        ///		return totalEmployeeCount;
        ///	}
        /// </code>
        /// </example>
        #endregion
        public static int CountTree(this IEntityDAO daoRoot, DbRelation recursiveRelation, SearchCondition leafFilter, int maxLevels)
        {
            if (maxLevels < 0)
                throw new ArgumentOutOfRangeException("maxLevels", Messages.EntityDAO_NegativeValueIsNotAllowed);

            return daoRoot.CountTree(recursiveRelation, leafFilter, 0, maxLevels - 1);
        }

        #region Documentation.
        /// <summary>
        /// Retrieves the sum of the column values in the hierarchy starting with the current entity.
        /// </summary>
        /// <param name="daoRoot">IEntityDAO object that manages the entity which represents a starting node, i.e. a parent in hierarchy.</param>
        /// <param name="column">Column. Must be a numeric column that belongs to the current entity's table.</param>
        /// <param name="recursiveRelation">Recursive relation which defines a hierarchy.</param>
        /// <param name="leafFilter">The search condition which restricts the rows processed. Often a criteria which filters out deactivated records.</param>
        /// <returns>Total sum of column values in the hierarchy or <see cref="DBNull.Value"/>.</returns>
        #endregion
        public static object SumTree(this IEntityDAO daoRoot, IDbColumn column, DbRelation recursiveRelation, SearchCondition leafFilter)
        {
            return daoRoot.SumTree(column, recursiveRelation, leafFilter, 0, int.MaxValue);
        }

        #region Documentation.
        /// <summary>
        /// Retrieves the sum of the column values in the hierarchy starting with the current entity.
        /// </summary>
        /// <param name="daoRoot">IEntityDAO object that manages the entity which represents a starting node, i.e. a parent in hierarchy.</param>
        /// <param name="column">Column. Must be a numeric column that belongs to the current entity's table.</param>
        /// <param name="recursiveRelation">Recursive relation which defines a hierarchy.</param>
        /// <param name="leafFilter">The search condition which restricts the rows processed. Often a criteria which filters out deactivated records.</param>
        /// <param name="maxLevels">Maximum number of tree levels to process. One specifies that only current entity is summed, 
        /// two specifies that current entity and entities directly beneath it are summed etc.</param>
        /// <returns>Total sum of column values in the hierarchy or <see cref="DBNull.Value"/>.</returns>
        #endregion
        public static object SumTree(this IEntityDAO daoRoot, IDbColumn column, DbRelation recursiveRelation, SearchCondition leafFilter, int maxLevels)
        {
            if (maxLevels < 0)
                throw new ArgumentOutOfRangeException("maxLevels", Messages.EntityDAO_NegativeValueIsNotAllowed);

            return daoRoot.SumTree(column, recursiveRelation, leafFilter, 0, maxLevels - 1);
        }

        #region Documentation.
        /// <summary>
        /// Retrieves the minimum column value in the hierarchy starting with the current entity.
        /// </summary>
        /// <param name="daoRoot">IEntityDAO object that manages the entity which represents a starting node, i.e. a parent in hierarchy.</param>
        /// <param name="column">Column to compare. Must belong to the current entity's table.</param>
        /// <param name="recursiveRelation">Recursive relation which defines a hierarchy.</param>
        /// <param name="leafFilter">The search condition which restricts the rows processed. Often a criteria which filters out deactivated records.</param>
        /// <returns>Minimum value or <see cref="DBNull.Value"/>.</returns>
        /// <example>The following example retrieves the date of the oldest employee in the hierarchy starting with the specified employee/manager.
        /// <code>
        ///	public DateTime FindOldestInHierarchy(int managerId)
        ///	{
        ///		EmployeesEntity manager = new EmployeesEntity(managerId);
        ///		IEntityDAO dao = new EntityDAO(manager);
        ///		object result = dao.MinInTree(manager.Table.BirthDate, manager.Table.FK_ReportsTo, null);
        ///		return Convert.ToDateTime(result);
        ///	}
        /// </code>
        /// </example>
        #endregion
        public static object MinInTree(this IEntityDAO daoRoot, IDbColumn column, DbRelation recursiveRelation, SearchCondition leafFilter)
        {
            return daoRoot.MinInTree(column, recursiveRelation, leafFilter, 0, int.MaxValue);
        }

        #region Documentation.
        /// <summary>
        /// Retrieves the minimum column value in the hierarchy starting with the current entity.
        /// </summary>
        /// <param name="daoRoot">IEntityDAO object that manages the entity which represents a starting node, i.e. a parent in hierarchy.</param>
        /// <param name="column">Column to compare. Must belong to the current entity's table.</param>
        /// <param name="recursiveRelation">Recursive relation which defines a hierarchy.</param>
        /// <param name="leafFilter">The search condition which restricts the rows processed. Often a criteria which filters out deactivated records.</param>
        /// <param name="maxLevels">Maximum number of tree levels to process. One specifies that only current entity is compared, 
        /// two specifies that current entity and entities directly beneath it are compared etc.</param>
        /// <returns>Minimum value or <see cref="DBNull.Value"/>.</returns>
        /// <example>The following example retrieves the date of the oldest employee in the first three levels of the hierarchy starting with the specified employee/manager.
        /// <code>
        ///	public DateTime FindOldestInHierarchy(int managerId)
        ///	{
        ///		EmployeesEntity manager = new EmployeesEntity(managerId);
        ///		IEntityDAO dao = new EntityDAO(manager);
        ///		object result = dao.MinInTree(manager.Table.BirthDate, manager.Table.FK_ReportsTo, null, 3);
        ///		return Convert.ToDateTime(result);
        ///	}
        /// </code>
        /// </example>
        #endregion
        public static object MinInTree(this IEntityDAO daoRoot, IDbColumn column, DbRelation recursiveRelation, SearchCondition leafFilter, int maxLevels)
        {
            if (maxLevels < 0)
                throw new ArgumentOutOfRangeException("maxLevels", Messages.EntityDAO_NegativeValueIsNotAllowed);

            return daoRoot.MinInTree(column, recursiveRelation, leafFilter, 0, maxLevels - 1);
        }

        #region Documentation.
        /// <summary>
        /// Retrieves the maximum column value in the hierarchy starting with the current entity.
        /// </summary>
        /// <param name="daoRoot">IEntityDAO object that manages the entity which represents a starting node, i.e. a parent in hierarchy.</param>
        /// <param name="column">Column to compare. Must belong to the current entity's table.</param>
        /// <param name="recursiveRelation">Recursive relation which defines a hierarchy.</param>
        /// <param name="leafFilter">The search condition which restricts the rows processed. Often a criteria which filters out deactivated records.</param>
        /// <returns>Maximum value or <see cref="DBNull.Value"/>.</returns>
        /// <example>The following example retrieves the date of the youngest employee in the hierarchy starting with the specified employee/manager.
        /// <code>
        ///	public DateTime FindYoungestInHierarchy(int managerId)
        ///	{
        ///		EmployeesEntity manager = new EmployeesEntity(managerId);
        ///		IEntityDAO dao = new EntityDAO(manager);
        ///		object result = dao.MaxInTree(manager.Table.BirthDate, manager.Table.FK_ReportsTo, null);
        ///		return Convert.ToDateTime(result);
        ///	}
        /// </code>
        /// </example>
        #endregion
        public static object MaxInTree(this IEntityDAO daoRoot, IDbColumn column, DbRelation recursiveRelation, SearchCondition leafFilter)
        {
            return daoRoot.MaxInTree(column, recursiveRelation, leafFilter, 0, int.MaxValue);
        }

        #region Documentation.
        /// <summary>
        /// Retrieves the maximum column value in the hierarchy starting with the current entity.
        /// </summary>
        /// <param name="daoRoot">IEntityDAO object that manages the entity which represents a starting node, i.e. a parent in hierarchy.</param>
        /// <param name="column">Column to compare. Must belong to the current entity's table.</param>
        /// <param name="recursiveRelation">Recursive relation which defines a hierarchy.</param>
        /// <param name="leafFilter">The search condition which restricts the rows processed. Often a criteria which filters out deactivated records.</param>
        /// <param name="maxLevels">Maximum number of tree levels to process. One specifies that only current entity is compared, 
        /// two specifies that current entity and entities directly beneath it are compared etc.</param>
        /// <returns>Maximum value or <see cref="DBNull.Value"/>.</returns>
        /// <example>The following example retrieves the date of the oldest employee in the first three levels of the hierarchy starting with the specified employee/manager.
        /// <code>
        ///	public DateTime FindYoungestInHierarchy(int managerId)
        ///	{
        ///		EmployeesEntity manager = new EmployeesEntity(managerId);
        ///		IEntityDAO dao = new EntityDAO(manager);
        ///		object result = dao.MaxInTree(manager.Table.BirthDate, manager.Table.FK_ReportsTo, null, 3);
        ///		return Convert.ToDateTime(result);
        ///	}
        /// </code>
        /// </example>
        #endregion
        public static object MaxInTree(this IEntityDAO daoRoot, IDbColumn column, DbRelation recursiveRelation, SearchCondition leafFilter, int maxLevels)
        {
            if (maxLevels < 0)
                throw new ArgumentOutOfRangeException("maxLevels", Messages.EntityDAO_NegativeValueIsNotAllowed);

            return daoRoot.MaxInTree(column, recursiveRelation, leafFilter, 0, maxLevels - 1);
        }

        #endregion

        // Table methods.

        #region Delete.

        #region Documentation.
        /// <summary>
		/// Deletes all rows that match the filter criteria.
		/// </summary>
        /// <param name="dao">IEntityDAO object.</param>
		/// <param name="column">Column whose value must be equal to the given value to be deleted.</param>
		/// <param name="value">Value. May be <b>null</b>.</param>
		/// <returns>Number of rows affected. -1 if the operation was aborted.</returns>
		/// <remarks>The method creates a <see cref="SearchCondition"/> that compares the specified column 
		/// to the given value. Only the rows where the specified column matches the given value are deleted.</remarks>
		/// <example>
		/// The following example deletes a row from the Employees table and all rows in the EmployeeTerritories 
		/// table that reference it. All commands are executed as parts of a single transaction. Thus, either
		/// all data is deleted or none.
		/// <code>
		/// public void DeleteEmployeeAndChildEntities(int employeeId)
		/// {
		/// 	// Deletes an employee and all records from the EmployeeTerritories table bound to the
		/// 	// specified employee. As a single transaction. Either all data is deleted or none.
		/// 	
		/// 	// Employee witch is to be deleted and DAO that will perform operation.
		/// 	EmployeesEntity employee = new EmployeesEntity();
		/// 	employee.EmployeeID = employeeId;
		/// 	EntityDAO daoEmployees = new EntityDAO(employee);
		/// 	
		/// 	// Junction rows in EmployeeTerritories that reference the employee and 
		/// 	// DAO that works the EmployeeTerritories table.
		/// 	EmployeeTerritoriesEntity empTerritories = new EmployeeTerritoriesEntity();
		/// 	EntityDAO daoJunctionTable = new EntityDAO(empTerritories);
		/// 	
		/// 	try		
		/// 	{
		/// 		// Open connection and begin transaction.
		/// 		// Connection and transaction context are sharet between two DAOs.
		/// 		daoEmployees.ConnectionProvider.BeginTransaction();
		/// 		
		/// 		// First, junction rows must be deleted where column "EmployeeID"
		/// 		// contains a value equal to the given employee's ID. 
		/// 		daoJunctionTable.ConnectionProvider = daoEmployees.ConnectionProvider;
		/// 		daoJunctionTable.Delete(empTerritories.Table.EmployeeID, employee.EmployeeID);
		/// 
		/// 		// Now we can safely delete employee, since it is no longer referenced.
		/// 		daoEmployees.DeleteOne();
		/// 		
		/// 		// Commit transaction.
		/// 		daoEmployees.ConnectionProvider.CommitTransaction();
		/// 	}
		/// 	catch	
		/// 	{
		/// 		// An error occured. Rollback the entire transaction and bubble up the exception that occured.
		/// 		daoEmployees.ConnectionProvider.RollbackTransaction();
		/// 		throw;
		/// 	}
		/// 	finally
		/// 	{
		/// 		// It is a good practice to close connection if it was openned manually.
		/// 		// The connection is returned to ADO.NET connection pool.
		/// 		daoEmployees.ConnectionProvider.CloseConnection();
		/// 	}
		/// }
		/// </code>
		/// </example>
		#endregion
		public static int Delete(this IEntityDAO dao, IDbColumn column, object value)
		{
            SearchCondition filter = new SearchCondition(column, value);
			return dao.Delete(filter);
		}

        #endregion

        #region Update.

        #region Documentation.
        /// <summary>
        /// Updates all rows that match the filter criteria.
        /// </summary>
        /// <param name="dao">IEntityDAO object.</param>
        /// <param name="column">Column whose value must be equal to the given old value to be updated.</param>
        /// <param name="oldValue">Old value of the column. May be <b>null</b>.</param>
        /// <param name="newValue">New value of the column. May be <b>null</b>.</param>
        /// <returns>Number of rows affected.</returns>
        /// <remarks>Updates the values in the specified column. Old value is changed to new value. 
        /// Often used to update a foreign key column.</remarks>
        /// <example>
        /// The following example updates ReportsTo field in the Employees table. An old value is changed to a new one.
        /// <code>
        /// public void ChangeBoss(int oldBossId, int newBossId)
        /// {
        /// 	EmployeesEntity emp = new EmployeesEntity();
        /// 	EntityDAO dao = new EntityDAO(emp);
        /// 	dao.Update(emp.Table.ReportsTo, oldBossId, newBossId);
        /// }
        /// </code>
        /// </example>
        #endregion
        public static int Update(this IEntityDAO dao, IDbColumn column, object oldValue, object newValue)
        {
            SearchCondition oldRows = new SearchCondition(column, oldValue);
            UpdateList setNew = new UpdateList(column, newValue);
            return dao.Update(setNew, oldRows);
        }

        #endregion

        #region Select.

        #region Documentation.
        /// <summary>
        /// Selects all rows from the table. Data is sorted if the sorter is set.	
        /// </summary>
        /// <param name="dao">IEntityDAO object.</param>
        /// <param name="destination">A collection to fill with entities built from data.</param>
        /// <example>
        /// The following examples selects all employees sorted by their last name.
        /// <code>
        /// public EntityCollection&lt;EmployeesEntity, EmployeesMeta&gt; SelectAllEmployeesOrderedByLastName()
        /// {
        /// 	EmployeesEntity emp = new EmployeesEntity();
        /// 	EntityDAO dao = new EntityDAO(emp);
        /// 	
        /// 	// Order by last name. Use ascending order (true).
        /// 	dao.SetSorter(new OrderByClause(emp.Table.LastName, true));
        /// 	var allEmployees = new EntityCollection&lt;EmployeesEntity, EmployeesMeta&gt;();
        /// 	dao.SelectAll(allEmployees);
        /// 	return allEmployees;
        /// }
        /// </code>
        /// </example>
        #endregion
        public static void SelectAll(this IEntityDAO dao, IList destination)
        {
            dao.Select(destination, null, null, -1);
        }

        #region Documentation.
        /// <summary>
        /// Selects all rows that match the filter criteria. Data is sorted if the sorter is set.
        /// </summary>
        /// <param name="dao">IEntityDAO object.</param>
        /// <param name="destination">A collection to fill with entities built from data.</param>
        /// <param name="column">Column whose value must be equal to the given value to be selected.</param>
        /// <param name="value">Value. May be <b>null</b>.</param>
        /// <remarks>The method creates a <see cref="SearchCondition"/> that compares the specified column 
        /// to the given value. Only the rows where the specified column matches the given value are selected.</remarks>
        /// <example>
        /// The following example selects all employees that live in New York.
        /// <code>
        /// public EntityCollection&lt;EmployeesEntity, EmployeesMeta&gt; SelectNewYorkers()
        /// {
        /// 	EmployeesEntity emp = new EmployeesEntity();
        /// 	EntityDAO dao = new EntityDAO(emp);
        /// 	var nyEmployees = new EntityCollection&lt;EmployeesEntity, EmployeesMeta&gt;();
        /// 	dao.Select(nyEmployees, emp.Table.City, "New York");
        /// 	return newYorkEmployees;
        /// }
        /// </code>
        /// </example>
        #endregion
        public static void Select(this IEntityDAO dao, IList destination, IDbColumn column, object value)
        {
            SearchCondition filter = new SearchCondition(column, value);
            dao.Select(destination, null, filter, -1);
        }

        #region Documentation.
        /// <summary>
        /// Selects all rows that match the filter criteria. Data is sorted if the sorter is set.
        /// </summary>
        /// <param name="dao">IEntityDAO object.</param>
        /// <param name="destination">A collection to fill with entities built from data.</param>
        /// <param name="relations">Relations. May be <b>null</b> if the filter doesn't use other tables.</param>
        /// <param name="filter">The search condition which restricts the rows returned.</param>
        /// <example>
        /// The following example uses the Select method that allows advanced filtering using RelationBucket 
        /// and SearchCondition objects. It selects all employees that are in charge of the New York territory.
        /// <code>
        /// public EntityCollection&lt;EmployeesEntity, EmployeesMeta&gt; SelectEmployeesInChargeOfNewYorkTerritory()
        /// {
        /// 	EmployeesEntity emp = new EmployeesEntity();
        /// 
        /// 	// Create a relation between Employees and EmployeeTerritories tables.
        /// 	// Employees is a parent table.
        /// 	// EmployeeTerritories is a child table.
        /// 	// EmployeeTerritories.EmployeeID is a foreign key.
        /// 	EmployeeTerritoriesMeta empTerritories = new EmployeeTerritoriesMeta();
        /// 	RelationBucket relations = new RelationBucket();
        /// 	relations.Add(new DbRelation(emp.Table, empTerritories, empTerritories.EmployeeID));
        /// 
        /// 	// Create a search condition with a single predicate.
        /// 	// Comparisson predicate compares TerritoryID field to given value (New York ID)
        /// 	// using the specified comparison operator ("=").
        /// 	SearchCondition territoryIdEqualsNy = new SearchCondition();
        /// 	territoryIdEqualsNy.Add(PredicateFactory.Compare(empTerritories.TerritoryID, "=", "10019"));
        /// 	
        /// 	// Select all employees that are in charge of the New York territory.
        /// 	EntityDAO dao = new EntityDAO(emp);
        /// 	var nyEmployees = new EntityCollection&lt;EmployeesEntity, EmployeesMeta&gt;();
        /// 	dao.Select(relations, territoryIdEqualsNy);
        /// 	return nyEmployees;
        /// }
        /// </code>
        /// </example>
        #endregion
        public static void Select(this IEntityDAO dao, IList destination, RelationBucket relations, SearchCondition filter)
        {
            dao.Select(destination, relations, filter, -1);
        }

        #region Documentation.
        /// <summary>
        /// Selects all rows from the table. Data is sorted if the sorter is set.	
        /// </summary>
        /// <param name="dao">IEntityDAO object.</param>
        /// <returns>All rows.</returns>
        /// <example>
        /// The following examples selects all employees sorted by their last name.
        /// <code>
        /// public DataTable SelectAllEmployeesOrderedByLastName()
        /// {
        /// 	EmployeesEntity emp = new EmployeesEntity();
        /// 	EntityDAO dao = new EntityDAO(emp);
        /// 	
        /// 	// Order by last name. Use ascending order (true).
        /// 	dao.SetSorter(new OrderByClause(emp.Table.LastName, true));
        /// 	DataTable allEmployees = dao.SelectAll();
        /// 	return allEmployees;
        /// }
        /// </code>
        /// </example>
        #endregion
        public static DataTable SelectAll(this IEntityDAO dao)
        {
            return dao.Select(null, null, -1);
        }

        #region Documentation.
        /// <summary>
        /// Selects all rows that match the filter criteria. Data is sorted if the sorter is set.
        /// </summary>
        /// <param name="dao">IEntityDAO object.</param>
        /// <param name="column">Column whose value must be equal to the given value to be selected.</param>
        /// <param name="value">Value. May be <b>null</b>.</param>
        /// <returns>Data.</returns>
        /// <remarks>The method creates a <see cref="SearchCondition"/> that compares the specified column 
        /// to the given value. Only the rows where the specified column matches the given value are selected.</remarks>
        /// <example>
        /// The following example selects all employees that live in New York.
        /// <code>
        /// public DataTable SelectNewYorkers()
        /// {
        /// 	EmployeesEntity emp = new EmployeesEntity();
        /// 	EntityDAO dao = new EntityDAO(emp);
        /// 	DataTable newYorkEmployees = dao.Select(emp.Table.City, "New York");
        /// 	return newYorkEmployees;
        /// }
        /// </code>
        /// </example>
        #endregion
        public static DataTable Select(this IEntityDAO dao, IDbColumn column, object value)
        {
            SearchCondition filter = new SearchCondition(column, value);
            return dao.Select(null, filter, -1);
        }

        #endregion

        #region Find.

        #region Documentation.
        /// <summary>
        /// Selects all rows that match an automatically created filter based on all properties that have been set.
        /// Predicates are combined by AND operators. Data is sorted if the sorter is set.
        /// </summary>
        /// <param name="dao">IEntityDAO object.</param>
        /// <param name="destination">A collection to fill with entities built from data.</param>
        /// <example>
        /// The following example select all employees whose first name is "John" and the last name is "Smith".
        /// <code>
        /// public EntityCollection&lt;EmployeesEntity, EmployeesMeta&gt; FindAllJohnSmiths()
        /// {
        /// 	EmployeesEntity emp = new EmployeesEntity();
        /// 	emp.FirstName = "John";
        /// 	emp.LastName = "Smith";
        /// 	
        /// 	EntityDAO dao = new EntityDAO(emp);
        /// 	var johnSmiths = new EntityCollection&lt;EmployeesEntity, EmployeesMeta&gt;();
        /// 	dao.Find(johnSmiths);
        /// 	return johnSmiths;
        /// }
        /// </code>
        /// </example>
        #endregion
        public static void Find(this IEntityDAO dao, IList destination)
        {
            dao.Find(destination, -1);
        }

        #region Documentation.
        /// <summary>
        /// Selects all rows that match the filter criteria. Data is sorted if the sorter is set.
        /// </summary>
        /// <param name="dao">IEntityDAO object.</param>
        /// <param name="relations">Relations. May be <b>null</b> if the filter doesn't use other tables.</param>
        /// <param name="filter">The search condition which restricts the rows returned.</param>
        /// <returns>Data that matches the filter.</returns>
        /// <example>
        /// The following example uses the Select method that allows advanced filtering using RelationBucket 
        /// and SearchCondition objects. It selects all employees that are in charge of the New York territory.
        /// <code>
        /// public DataTable SelectEmployeesInChargeOfNewYorkTerritory()
        /// {
        /// 	EmployeesEntity emp = new EmployeesEntity();
        /// 
        /// 	// Create a relation between Employees and EmployeeTerritories tables.
        /// 	// Employees is a parent table.
        /// 	// EmployeeTerritories is a child table.
        /// 	// EmployeeTerritories.EmployeeID is a foreign key.
        /// 	EmployeeTerritoriesMeta empTerritories = new EmployeeTerritoriesMeta();
        /// 	RelationBucket relations = new RelationBucket();
        /// 	relations.Add(new DbRelation(emp.Table, empTerritories, empTerritories.EmployeeID));
        /// 
        /// 	// Create a search condition with a single predicate.
        /// 	// Comparisson predicate compares TerritoryID field to given value (New York ID)
        /// 	// using the specified comparison operator ("=").
        /// 	SearchCondition territoryIdEqualsNy = new SearchCondition();
        /// 	territoryIdEqualsNy.Add(PredicateFactory.Compare(empTerritories.TerritoryID, "=", "10019"));
        /// 	
        /// 	// Select all employees that are in charge of the New York territory.
        /// 	EntityDAO dao = new EntityDAO(emp);
        /// 	DataTable nyEmployees = dao.Select(relations, territoryIdEqualsNy);
        /// 	return nyEmployees;
        /// }
        /// </code>
        /// </example>
        #endregion
        public static DataTable Select(this IEntityDAO dao, RelationBucket relations, SearchCondition filter)
        {
            return dao.Select(relations, filter, -1);
        }

        #region Documentation.
        /// <summary>
        /// Selects all rows that match an automatically created filter based on all properties that have been set.
        /// Predicates are combined by AND operators. Data is sorted if the sorter is set.
        /// </summary>
        /// <param name="dao">IEntityDAO object.</param>
        /// <returns>Rows that match the automatically created filter.</returns>
        /// <example>
        /// The following example select all employees whose first name is "John" and the last name is "Smith".
        /// <code>
        /// public DataTable FindAllJohnSmiths()
        /// {
        /// 	EmployeesEntity emp = new EmployeesEntity();
        /// 	emp.FirstName = "John";
        /// 	emp.LastName = "Smith";
        /// 	
        /// 	EntityDAO dao = new EntityDAO(emp);
        /// 	return dao.Find();
        /// }
        /// </code>
        /// </example>
        #endregion
        public static DataTable Find(this IEntityDAO dao)
        {
            return dao.Find(-1);
        }

        #region Documentation.
        /// <summary>
        /// Selects all rows that match an automatically created filter based on all properties that have been set. 
        /// Predicates are combined by OR operators. Data is sorted if the sorter is set.
        /// </summary>
        /// <param name="dao">IEntityDAO object.</param>
        /// <param name="destination">A collection to fill with entities built from data.</param>
        #endregion
        public static void FindMatchingAnyCriteria(this IEntityDAO dao, IList destination)
        {
            dao.FindMatchingAnyCriteria(destination, -1);
        }

        #region Documentation.
        /// <summary>
        /// Selects all rows that match an automatically created filter based on all properties that have been set. 
        /// Predicates are combined by OR operators. Data is sorted if the sorter is set.
        /// </summary>
        /// <param name="dao">IEntityDAO object.</param>
        /// <returns>Rows that match the automatically created filter.</returns>
        #endregion
        public static DataTable FindMatchingAnyCriteria(this IEntityDAO dao)
        {
            return dao.FindMatchingAnyCriteria(-1);
        }

        #endregion

        #region Aggregate functions.

        #region Documentation.
        /// <summary>
        /// Counts all rows in the entity's parent table that match the filter criteria.
        /// </summary>
        /// <param name="dao">IEntityDAO object.</param>
        /// <param name="column">Column whose value must be equal to the given value to be included in the count.</param>
        /// <param name="value">Value. May be <b>null</b>.</param>
        /// <returns>Number of rows that match the filter.</returns>
        /// <remarks>The method creates a <see cref="SearchCondition"/> that compares the specified column to 
        /// the given value. Only the rows where the specified column matches the given value are counted.</remarks>
        /// <example>
        /// The following example returns the number of employees that live in New York.
        /// <code>
        /// public int CountNewYorkers()
        /// {
        /// 	EmployeesEntity emp = new EmployeesEntity();
        /// 	EntityDAO dao = new EntityDAO(emp);
        /// 	int count = dao.Count(emp.Table.City, "New York");
        /// 	return count;
        /// }
        /// </code>
        /// </example>
        #endregion
        public static int Count(this IEntityDAO dao, IDbColumn column, object value)
        {
            SearchCondition filter = new SearchCondition(column, value);
            return dao.Count(null, filter);
        }

        #region Documentation.
        /// <summary>
        /// Retrieves the maximum value in the given column.
        /// </summary>
        /// <param name="dao">IEntityDAO object.</param>
        /// <param name="column">Column. Must belong to the current entity's table.</param>
        /// <returns>Maximum value in the given column. <see cref="DBNull.Value"/> if table doesn't contain any rows or 
        /// if all rows have a <b>null</b> value in the specified field.</returns>
        /// <example>
        /// <code>
        /// public DateTime FetchYoungest()
        ///{
        ///    EmployeesMeta emp = new EmployeesMeta();
        ///    IEntityDAO dao = new EntityDAO(emp);
        ///    return (DateTime)dao.Max(emp.BirthDate);
        ///}
        ///</code>
        ///</example>
        #endregion
        public static object Max(this IEntityDAO dao, IDbColumn column)
        {
            return dao.Max(column, null, null);
        }

        #region Documentation.
        /// <summary>
        /// Retrieves the minimum value in the given column.
        /// </summary>
        /// <param name="dao">IEntityDAO object.</param>
        /// <param name="column">Column. Must belong to the current entity's table.</param>
        /// <returns>Minimum value in the given column. <see cref="DBNull.Value"/> if table doesn't contain any rows or 
        /// if all rows have a <b>null</b> value in the specified field.</returns>
        /// <example>
        /// <code>
        /// public DateTime FetchOldest()
        ///{
        ///    EmployeesMeta emp = new EmployeesMeta();
        ///    IEntityDAO dao = new EntityDAO(emp);
        ///    return (DateTime)dao.Min(emp.BirthDate);
        ///}
        ///</code>
        ///</example> 
        #endregion
        public static object Min(this IEntityDAO dao, IDbColumn column)
        {
            return dao.Min(column, null, null);
        }

        #region Documentation.
        /// <summary>
        /// Retrieves the sum of the values in the given column.
        /// </summary>
        /// <param name="dao">IEntityDAO object.</param>
        /// <param name="column">Column. Must be a numeric column that belongs to the current entity's table.</param>
        /// <returns>The sum of the values in the given column. <see cref="DBNull.Value"/> if table doesn't contain any rows or 
        /// if all rows have a <b>null</b> value in the specified field.</returns>
        /// <remarks>SUM can be used with numeric columns only. Null values are ignored.</remarks>
        #endregion
        public static object Sum(this IEntityDAO dao, IDbColumn column)
        {
            return dao.Sum(column, null, null);
        }

        #region Documentation.
        /// <summary>
        /// Retrieves the average of the values in the given column.
        /// </summary>
        /// <param name="dao">IEntityDAO object.</param>
        /// <param name="column">Column. Must be a numeric column that belongs to the current entity's table.</param>
        /// <returns>The average of the values in the given column. <see cref="DBNull.Value"/> if table doesn't contain any rows or 
        /// if all rows that match the criteria have a <b>null</b> value in the specified field.</returns>
        /// <remarks>AVG can be used with numeric columns only. Null values are ignored.</remarks>
        /// <example>
        /// <code>
        /// public DateTime AvgProductPrice(emp)
        ///{
        ///    ProductsMeta prod = new ProductsMeta();
        ///    IEntityDAO dao = new EntityDAO(prod);
        ///    object avg = DAL.GetDao(prod).Avg(prod.UnitPrice);
        ///    return Convert.ToDecimal(avg);
        ///}
        ///</code>
        ///</example>  
        #endregion
        public static object Avg(this IEntityDAO dao, IDbColumn column)
        {
            return dao.Avg(column, null, null);
        }

        #endregion

        #region IsSubNode.

        /// <summary>Checks whether one record in the hiearchical table is below the other one.</summary>
        /// <param name="daoParent">IEntityDAO object that manages the entity which represents a starting node, i.e. a parent in hierarchy.</param>
        /// <param name="recursiveRelation">Recursive relation which defines a hierarchy.</param>
        /// <param name="subNode">A node which references the parent node either directly or through other records which belong to the same (sub)tree.</param>
        /// <returns><b>true</b> if the sub-node belongs to that same tree as the parent node; otherwise <b>false</b>.</returns>
        /// <remarks><para>The method also returns <b>true</b> if both specified nodes represent the same record, i.e. have equal primary keys.</para>
        /// <para>The term sub-node is used for a node which references the parent node either directly or through other records which belong to the same (sub)tree.</para>
        /// <para>Note, this method only tests data which are already persisted in the data source.
        /// It doesn't analyze unpersisted, i.e. new or changed, data set to entities' properties.</para></remarks>
        public static bool IsSubNode(this IEntityDAO daoParent, DbRelation recursiveRelation, IEntity subNode)
        {
            SearchCondition subNodeFilter = DaoUtil.CreatePrimaryKeyFilter(subNode);
            int count = daoParent.CountTree(recursiveRelation, subNodeFilter);
            return (count > 0);
        }

        #endregion

        #region MaxPlusOne.

        /// <summary>Fetches current maximum integer value in the specified column and increases it by one.</summary>
        /// <param name="dao">IEntityDAO object.</param>
        /// <param name="column">A column that contains integer values.</param>
        /// <returns>Current maximum value increased by one. If no records matching given filter are found, then 1 is returned.</returns>
        public static long MaxPlusOne(this IEntityDAO dao, IDbColumn column)
        {
            return MaxPlusOne(dao, column, null);
        }

        /// <summary>Fetches current maximum integer value in the specified column and increases it by one.</summary>
        /// <param name="dao">IEntityDAO object.</param>
        /// <param name="column">A column that contains integer values.</param>
        /// <param name="filter">Optional row filter.</param>
        /// <returns>Current maximum value increased by one. If no records matching given filter are found, then 1 is returned.</returns>
        public static long MaxPlusOne(this IEntityDAO dao, IDbColumn column, SearchCondition filter)
        {
            if (!TypeUtil.IsInteger(column.DataType))
                throw new ArgumentException("Provided column must contain integer data.");

            object currMax = dao.Max(column, null, filter);
            if (currMax == null || currMax == DBNull.Value)
                return 1;
            else
                return 1 + Convert.ToInt64(currMax);
        }

        #endregion

        #region DeleteEntitiesRemovedFromCollection.

        /// <summary>Deletes entities which were removed from a collection.</summary>
        /// <typeparam name="T">Type of values used in primary key field.</typeparam>
        /// <param name="dao">IEntityDAO object.</param>
        /// <param name="modifiedCollection">Collection from which entities have been removed.</param>
        /// <param name="oldIDs">Array of IDs before the collection was modified. May be <b>null</b> or empty.</param>
        public static void DeleteEntitiesRemovedFromCollection<T>(this IEntityDAO dao, IEnumerable modifiedCollection, T[] oldIDs)
        {
            IDbTable table = dao.Table;
            if (table.PrimaryKey.Count != 1)
                throw new ArgumentException("Primary key must be constructed of exactly one column.");

            IDbColumn pk = table.PrimaryKey[0];
            if (ArrayUtil.IsNullOrEmpty(oldIDs))
                return;

            T[] remainingIDs = ArrayUtil.ToArray<T>(modifiedCollection, pk.PropertyName);
            var removedIDs = ArrayUtil.GetDifference<List<T>, T>(oldIDs, remainingIDs);
            if (ArrayUtil.IsNullOrEmpty(removedIDs))
                return;

            dao.Delete((SearchCondition)PredicateFactory.In(pk, removedIDs.ToArray()));
        }

        #endregion

        #region UpdateStatusOfEntititesRemovedFromCollection.

        /// <summary>Updates status of entities which were remove from a collection.</summary>
        /// <typeparam name="T">Type of values used in primary key field.</typeparam>
        /// <param name="dao">IEntityDAO object.</param>
        /// <param name="modifiedCollection">Collection from which entities have been removed.</param>
        /// <param name="oldIDs">Array of IDs before the collection was modified. May be <b>null</b> or empty.</param>
        /// <param name="field">Field to update. Typically a status (0/1) or a foreign key field.</param>
        /// <param name="newFieldValue">Value to set to field.</param>
        public static void UpdateEntitiesRemovedFromCollection<T>(this IEntityDAO dao, IEnumerable modifiedCollection, T[] oldIDs, IDbColumn field, object newFieldValue)
        {
            UpdateEntitiesRemovedFromCollection(dao, modifiedCollection, oldIDs, new UpdateList(field, newFieldValue));
        }

        /// <summary>Updates status of entities which were remove from a collection.</summary>
        /// <typeparam name="T">Type of values used in primary key field.</typeparam>
        /// <param name="dao">IEntityDAO object.</param>
        /// <param name="modifiedCollection">Collection from which entities have been removed.</param>
        /// <param name="oldIDs">Array of IDs before the collection was modified. May be <b>null</b> or empty.</param>
        /// <param name="setExpressions">A list of field-value pairs to update.</param>
        public static void UpdateEntitiesRemovedFromCollection<T>(this IEntityDAO dao, IEnumerable modifiedCollection, T[] oldIDs, UpdateList setExpressions)
        {
            IDbTable table = dao.Table;
            if (table.PrimaryKey.Count != 1)
                throw new ArgumentException("Primary key must be constructed of exactly one column.");

            IDbColumn pk = table.PrimaryKey[0];
            if (ArrayUtil.IsNullOrEmpty(oldIDs))
                return;

            T[] remainingIDs = ArrayUtil.ToArray<T>(modifiedCollection, pk.PropertyName);
            var removedIDs = ArrayUtil.GetDifference<List<T>, T>(oldIDs, remainingIDs);
            if (ArrayUtil.IsNullOrEmpty(removedIDs))
                return;

            dao.Update(setExpressions, (SearchCondition)PredicateFactory.In(pk, removedIDs.ToArray()));
        }

        #endregion

        #region UpdateOne.

        /// <summary>Updates only the specified fields of an entity.</summary>
        /// <param name="dao">IEntityDAO object.</param>
        /// <param name="fieldsToUpdate">FieldPermissions to update.</param>
        public static void UpdateOne(this IEntityDAO dao, params IDbColumn[] fieldsToUpdate)
        {
            IEntity entity = dao.Entity;
            UpdateStatement update = new UpdateStatement(entity.Table);
            foreach (IDbColumn field in fieldsToUpdate)
                update.UpdateList.Add(field, entity.GetField(field));
            foreach (IDbColumn pkPart in entity.Table.PrimaryKey)
                update.Where.Add(pkPart, entity.GetField(pkPart));

            update.Execute(dao.ConnectionProvider);
        }

        #endregion

        #region IsUnique.

        /// <summary>Checks whether the entity obeys unique constraint.</summary>
        /// <param name="dao">IEntityDAO object with reference to new or existing entity which is to be inspected.</param>
        /// <param name="uniqueField">Field which contains unique values.</param>
        /// <param name="filter">Row filter. Typically a condition which filters out deactivated records.</param>
        /// <returns><b>true</b> if the provided entity is the only one with the specified unique field value; <b>false</b> otherwise.</returns>
        public static bool IsUnique(this IEntityDAO dao, IDbColumn uniqueField, SearchCondition filter)
        {
            IEntity entity = dao.Entity;
            SearchCondition sameUniqueValue = new SearchCondition();
            sameUniqueValue.Add(uniqueField, entity.GetField(uniqueField));

            if (entity.EntityState != EntityState.New)
            {
                foreach (IDbColumn pkPart in entity.Table.PrimaryKey)
                    sameUniqueValue.Add(PredicateFactory.Compare(pkPart, ComparisonOperator.NotEqual, entity.GetField(pkPart)));
            }

            if (filter != null && !filter.IsEmpty)
                sameUniqueValue.Add(filter);

            int count = dao.Count(null, sameUniqueValue);
            bool isUnique = (count == 0);
            return isUnique;
        }

        /// <summary>Checks whether the entity obeys unique constraint.</summary>
        /// <param name="dao">IEntityDAO object with reference to new or existing entity which is to be inspected.</param>
        /// <param name="uniqueFields">Fields which construct a unique value combination.</param>
        /// <param name="filter">Row filter. Typically a condition which filters out deactivated records.</param>
        /// <returns><b>true</b> if the provided entity is the only one with the specified unique field value; <b>false</b> otherwise.</returns>
        public static bool IsUnique(this IEntityDAO dao, IDbColumn[] uniqueFields, SearchCondition filter)
        {
            IEntity entity = dao.Entity;
            SearchCondition sameUniqueValue = new SearchCondition();
            foreach (IDbColumn uniqueConstraintPart in uniqueFields)
                sameUniqueValue.And(uniqueConstraintPart, entity.GetField(uniqueConstraintPart));

            if (entity.EntityState != EntityState.New)
            {
                foreach (IDbColumn pkPart in entity.Table.PrimaryKey)
                    sameUniqueValue.Add(PredicateFactory.Compare(pkPart, ComparisonOperator.NotEqual, entity.GetField(pkPart)));
            }

            if (filter != null && !filter.IsEmpty)
                sameUniqueValue.Add(filter);

            int count = dao.Count(null, sameUniqueValue);
            bool isUnique = (count == 0);
            return isUnique;
        }

        #endregion

        #region FetchChildrenWithParents.

        /// <summary>Fetches and sorts child entities of entity worked by <b>daoParent</b> and fetches other parents.</summary>
        /// <typeparam name="T">Type of collection to return.</typeparam>
        /// <param name="daoParent">DAO which works with the parent entity.</param>
        /// <param name="relation">Relation between the parent entity worked by <b>daoParent</b> and child table.</param>
        /// <param name="orderBy">Optional field by which child are to be sorted. Must belong to child table.</param>
        /// <param name="relationsToOtherParents">Optional array of other parents which are to be fetched.</param>
        /// <returns>Sorted child collection with other parent objects.</returns>
        /// <example><code>
        /// public List&lt;OrderDetailsEntity&gt; FetchOrderDetailsWithProducts(OrdersEntity order)
        /// {
        ///     IEntityDAO daoParent = new EntityDAO(order);
        ///     OrderDetailsMeta items = new OrderDetailsMeta();
        ///     return daoParent.FetchChildrenWithParents&lt;List&lt;OrderDetailsEntity&gt;&gt;(items.FK_OrderID, items.ProductID, items.FK_ProductID);
        /// }
        /// </code></example>
        public static T FetchChildrenWithParents<T>(this IEntityDAO daoParent, DbRelation relation, IDbColumn orderBy, params DbRelation[] relationsToOtherParents)
            where T : IList, new()
        {
            return FetchChildrenWithParents<T>(daoParent, relation, orderBy, /*asc*/true, null, null, relationsToOtherParents);
        }

        /// <summary>Fetches and sorts child entities of entity worked by <b>daoParent</b> and fetches other parents.</summary>
        /// <typeparam name="T">Type of collection to return.</typeparam>
        /// <param name="daoParent">DAO which works with the parent entity.</param>
        /// <param name="relation">Relation between the parent entity worked by <b>daoParent</b> and child table.</param>
        /// <param name="orderBy">Optional field by which child are to be sorted. Must belong to child table.</param>
        /// <param name="ascending">Specifies whether the values in the specified column should be sorted in ascendin order, from lowest value to highest value.</param>
        /// <param name="relationsToOtherParents">Optional array of other parents which are to be fetched.</param>
        /// <returns>Sorted child collection with other parent objects.</returns>
        /// <example><code>
        /// public List&lt;OrderDetailsEntity&gt; FetchOrderDetailsWithProducts(OrdersEntity order)
        /// {
        ///     IEntityDAO daoParent = new EntityDAO(order);
        ///     OrderDetailsMeta items = new OrderDetailsMeta();
        ///     return daoParent.FetchChildrenWithParents&lt;List&lt;OrderDetailsEntity&gt;&gt;(items.FK_OrderID, items.ProductID, items.FK_ProductID);
        /// }
        /// </code></example>
        public static T FetchChildrenWithParents<T>(this IEntityDAO daoParent, DbRelation relation, IDbColumn orderBy, bool ascending, params DbRelation[] relationsToOtherParents)
            where T : IList, new()
        {
            return FetchChildrenWithParents<T>(daoParent, relation, orderBy, ascending, null, null, relationsToOtherParents);
        }

        /// <summary>Fetches, filters and sorts child entities of entity worked by <b>daoParent</b> and fetches other parents.</summary>
        /// <typeparam name="T">Type of collection to return.</typeparam>
        /// <param name="daoParent">DAO which works with the parent entity.</param>
        /// <param name="relation">Relation between the parent entity worked by <b>daoParent</b> and child table.</param>
        /// <param name="orderBy">Optional field by which child are to be sorted. Must belong to child table.</param>
        /// <param name="childField">Column whose value must be equal to the given value to be selected.</param>
        /// <param name="childFieldValue">Column value. May be null.</param>
        /// <param name="relationsToOtherParents">Optional array of other parents which are to be fetched.</param>
        /// <returns>Sorted child collection which match the criteria with other parent objects.</returns>
        /// <example><code>
        /// public List&lt;OrderDetailsEntity&gt; FetchOrderDetailsWithProducts(OrdersEntity order)
        /// {
        ///     IEntityDAO daoParent = new EntityDAO(order);
        ///     OrderDetailsMeta items = new OrderDetailsMeta();
        ///     return daoParent.FetchChildrenWithParents&lt;List&lt;OrderDetailsEntity&gt;&gt;(items.FK_OrderID, items.ProductID, items.FK_ProductID);
        /// }
        /// </code></example>
        public static T FetchChildrenWithParents<T>(this IEntityDAO daoParent, DbRelation relation, IDbColumn orderBy, IDbColumn childField, object childFieldValue, params DbRelation[] relationsToOtherParents)
            where T : IList, new()
        {
            return FetchChildrenWithParents<T>(daoParent, relation, orderBy, /*asc*/true, childField, childFieldValue, relationsToOtherParents);
        }

        /// <summary>Fetches, filters and sorts child entities of entity worked by <b>daoParent</b> and fetches other parents.</summary>
        /// <typeparam name="T">Type of collection to return.</typeparam>
        /// <param name="daoParent">DAO which works with the parent entity.</param>
        /// <param name="relation">Relation between the parent entity worked by <b>daoParent</b> and child table.</param>
        /// <param name="orderBy">Optional field by which child are to be sorted. Must belong to child table.</param>
        /// <param name="ascending">Specifies whether the values in the specified column should be sorted in ascendin order, from lowest value to highest value.</param>
        /// <param name="childField">Column whose value must be equal to the given value to be selected.</param>
        /// <param name="childFieldValue">Column value. May be null.</param>
        /// <param name="relationsToOtherParents">Optional array of other parents which are to be fetched.</param>
        /// <returns>Sorted child collection which match the criteria with other parent objects.</returns>
        /// <example><code>
        /// public List&lt;OrderDetailsEntity&gt; FetchOrderDetailsWithProducts(OrdersEntity order)
        /// {
        ///     IEntityDAO daoParent = new EntityDAO(order);
        ///     OrderDetailsMeta items = new OrderDetailsMeta();
        ///     return daoParent.FetchChildrenWithParents&lt;List&lt;OrderDetailsEntity&gt;&gt;(items.FK_OrderID, items.ProductID, items.FK_ProductID);
        /// }
        /// </code></example>
        public static T FetchChildrenWithParents<T>(this IEntityDAO daoParent, DbRelation relation, IDbColumn orderBy, bool ascending, IDbColumn childField, object childFieldValue, params DbRelation[] relationsToOtherParents)
            where T : IList, new()
        {
            T children = new T();            
            OrderByClause sorter = (orderBy != null) ? new OrderByClause(orderBy, ascending) : null;
            if (childField != null)
                FetchChildren(daoParent, children, relation, childField, childFieldValue, sorter);
            else
                FetchChildren(daoParent, children, relation, sorter);

            if (children.Count > 0)
            {
                IEntityDAO daoChildren = EntityDaoFactory.GetEntityDAO((IEntity)children[0], daoParent.ConnectionProvider);
                foreach (DbRelation fkToOtherParent in relationsToOtherParents)
                {
                    // Optimize if non-composite keys are used.
                    bool hasCompositeKeys = (relation.ChildForeignKey.Length > 1) || (fkToOtherParent.ChildForeignKey.Length > 1);
                    if (!hasCompositeKeys)
                    {
                        // In recursive relations it is possible that other parent is from the same table as child entities whose table is used as junction table.
                        DbRelation childToOther;
                        bool isRecursiveParentFromChildTable = (fkToOtherParent.Parent.TableName == relation.Child.TableName);
                        if (isRecursiveParentFromChildTable)
                            childToOther = fkToOtherParent.Clone(null, false, fkToOtherParent.Parent.TableName + "2", false);
                        else
                            childToOther = fkToOtherParent;

                        ManyToManyRelation currToOtherParent = new ManyToManyRelation(relation, childToOther);
                        if (childField != null)
                        {
                            currToOtherParent.JunctionRowsFilter = new SearchCondition();
                            currToOtherParent.JunctionRowsFilter.Add(childField, childFieldValue);
                        }

                        EntityCollection otherParents = new EntityCollection(childToOther.Parent);
                        DataTable otherParentsData = daoParent.FetchChildren(currToOtherParent, PrimaryTableIndex.FirstPrimaryTable, null);
                        otherParents.FromDataTable(otherParentsData);
                        children.SetParents(childToOther, otherParents, /*shared*/ true);
                    }
                    else
                    {
                        daoChildren.FetchParentsForCollection(children, fkToOtherParent);
                    }
                }
            }

            return children;
        }

        private static DataTable SortDataTable(DataTable data, string column, bool ascending)
        {
            string sort = new OrderByClause(column, ascending).GetDataViewSortExpression();
            DataTable sortedTable = data.Clone();
            DataRow[] sortedRows = data.Select(null, sort);
            foreach (DataRow row in sortedRows)
                sortedTable.ImportRow(row);

            return sortedTable;
        }

        #endregion
    }
}
